package ExecuteEngine;

import java.util.*;

import Const.Constant;
import RecordManagement.ScanType;
import parse.*;

public class Tuple {
	public List<Element> tuple = new ArrayList<Element>();
	
	public void addElement(Element e){
		tuple.add(e);
	}
	
	public Tuple(){
	}
	
	public Tuple(byte[] stream){
		int count = 0;
		
		while(count < stream.length){
			Element e = new Element();
			
			/* Get the relation name */
			byte data[] = new byte[Constant.MAXNAME];
			System.arraycopy(stream, count, data, 0, Constant.MAXNAME);
			
			String rel = new String(data);
		    e.relName = Tuple.Trim(rel);
		    count += Constant.MAXNAME;
		    
		    
		    /* Get the attribute name */
			data = new byte[Constant.MAXNAME];
			System.arraycopy(stream, count, data, 0, Constant.MAXNAME);
			
			String attr = new String(data);
		    e.attrName = Tuple.Trim(attr);
		    count += Constant.MAXNAME;
		    
		    
		    /* Get the type of the element */
		    data = new byte[4];
			System.arraycopy(stream, count, data, 0, Constant.INTSIZE);
			int type = Tuple.BytesToInt(data);
			count += Constant.INTSIZE;
			
			/* Get the length of the element */
		    data = new byte[4];
			System.arraycopy(stream, count, data, 0, Constant.INTSIZE);
			int length = Tuple.BytesToInt(data);
			e.length = length;
			count += Constant.INTSIZE;
			
			/* Get the value */
		    data = new byte[length];
			System.arraycopy(stream, count, data, 0, length);
			
			if(type == ScanType.TYPE_INT){
				int value = Tuple.BytesToInt(data);
				e.value = new IntegerItem(value);
			}
			else{
				String v = new String(data);
			    StringBuffer s = new StringBuffer(Tuple.Trim(v));
			    e.value = new VarcharItem(s);
			}
			count += length;
			
			this.tuple.add(e);
		}
	}
	
	public Tuple(Tuple t){
		for(int i=0; i<t.tuple.size(); i++)
			this.tuple.add(t.tuple.get(i));
	}
	
	public Tuple addTuple(Tuple t){
		for(int i=0; i<t.tuple.size(); i++)
			this.tuple.add(t.tuple.get(i));
		
		return this;
	}
	
	public static byte[] tupleToByte(DataItem[] t){
		byte record[];
		int recSize = 0;
		int position = 0;
		
		for(int i=0; i<t.length; i++){
			if(t[i] instanceof IntegerItem)
				recSize += 4;
			else
				recSize += ((VarcharItem)t[i]).value.length();
		}
		
		record = new byte[recSize];
		for(int i=0; i<t.length; i++){
			if(t[i] instanceof IntegerItem){
				byte intRec[] = new byte[4];
				for(int j = 0;j < 4;j++)
					intRec[j] = (byte)((((IntegerItem)t[i]).value) >> (24 - j * 8)); 
				System.arraycopy(intRec, 0, record, position, 4);
				position += 4;
			}
			else{
				int length = ((VarcharItem)t[i]).value.length();
				byte stringRec[] = new byte[length];
				String rec = ((VarcharItem)t[i]).value.toString();
				stringRec = rec.getBytes();
				System.arraycopy(stringRec, 0, record, position, length);
				position += length;
			}
		}
		
		return record;
	}
	
	public byte[] getBytes(){
		int count = 0;
		for(int i=0; i<tuple.size(); i++){
			count = count + Constant.MAXNAME*2 + Constant.INTSIZE*2 + tuple.get(i).length;
		}
		byte stream_byte[] = new byte[count];
		int offset = 0;
		
		for(int i=0; i<tuple.size(); i++){
			Element e = tuple.get(i);
			
			StringBuffer temp = new StringBuffer(e.relName);
			temp.setLength(Constant.MAXNAME);
			System.arraycopy(temp.toString().getBytes(), 0, stream_byte, offset, Constant.MAXNAME);
			offset += Constant.MAXNAME;
			
			temp = new StringBuffer(e.attrName);
			temp.setLength(Constant.MAXNAME);
			System.arraycopy(temp.toString().getBytes(), 0, stream_byte, offset, Constant.MAXNAME);
			offset += Constant.MAXNAME;
			
			int type = 0;
			if(e.value instanceof IntegerItem)
				type = ScanType.TYPE_INT;
			else
				type = ScanType.TYPE_STRING;
			
			byte[] c = Tuple.IntToBytes(type);
			System.arraycopy(c, 0, stream_byte, offset, Constant.INTSIZE);
			offset += Constant.INTSIZE;
			
			int length = e.length;
			c = Tuple.IntToBytes(length);
			System.arraycopy(c, 0, stream_byte, offset, Constant.INTSIZE);
			offset += Constant.INTSIZE;
			
			if(type == ScanType.TYPE_INT){
				int value = ((IntegerItem)e.value).value;
				c = Tuple.IntToBytes(value);
				
				System.arraycopy(c, 0, stream_byte, offset, Constant.INTSIZE);
				offset += Constant.INTSIZE;
			}
			else{
				String value = ((VarcharItem)e.value).value.toString();
				System.arraycopy(value.getBytes(), 0, stream_byte, offset, length);
				offset += length;
			}
		}
		
		return stream_byte;
	}

	public static String Trim(String s){
		String result = new String();
	    for(int j=0; j<s.length(); j++){
	    	if(s.charAt(j) != 0){
				char c[] = new char[1];
				c[0] = s.charAt(j);
				result+=new String(c);
	    	}
	    	else
	    		break;
	    }
	    
	    return result;
	}

	public static int BytesToInt(byte b[]){
		int n = 0;
		for(int j=0; j<4; j++){
			if(b[j] < 0)
				n += 256*(4-j);
		}
		n += (((int)b[0]) << 24) + (((int)b[1]) << 16) + (((int)b[2]) << 8) + b[3]; 
		
		return n;
	}

	public static byte[] IntToBytes(int n){
		byte[] c = new byte[4]; 
		for(int j = 0;j < 4;j++){ 
		  c[j] = (byte)(n >> (24 - j * 8)); 
		} 
		
		return c;
	}
}
